#include "Node.h"
#include "gameworld.h"
#include <glee\GLee.h>
#include "Physics.h"
#include "AABBCollider.h"

Node::Node(Node* parent, int levels, int totalLevels, float x, float y, float z, float rad)
{
	m_parent = parent;
	location.x = x;
	location.y = y;
	location.z = z;
	size = rad;

	level = totalLevels - levels - 1;
	numChildren = 0;
	entities = new std::list<Entity*>();
	clumps = new std::list<TerrainClump*>();
	if(levels > 0){
		int nextLevel = level + 1;
		float dist = GameWorld::OCTREE_SIZE / (pow(2.0f,nextLevel + 1));
		//rad = sqrt(dist*dist + dist*dist);
		//rad = sqrt(rad*rad + dist*dist);

		for(int i=0; i<9; i++){
			switch(i){
				case 0:
					x = location.x - dist;
					y = location.y - dist;
					z = location.z + dist;
					break;
				case 1:
					x = location.x - dist;
					y = location.y - dist;
					z = location.z - dist;
					break;
				case 2:
					x = location.x + dist;
					y = location.y - dist;
					z = location.z - dist;
					break;
				case 3:
					x = location.x + dist;
					y = location.y - dist;
					z = location.z + dist;
					break;
				case 4:
					x = location.x - dist;
					y = location.y + dist;
					z = location.z + dist;
					break;
				case 5:
					x = location.x - dist;
					y = location.y + dist;
					z = location.z - dist;
					break;
				case 6:
					x = location.x + dist;
					y = location.y + dist;
					z = location.z - dist;
					break;
				case 7:
					x = location.x + dist;
					y = location.y + dist;
					z = location.z + dist;
					break;
			}
			addChild(this,levels-1, totalLevels,x,y,z,dist);
		}
	}
}


Node::~Node()
{
	entities->clear();
	delete entities;
	for(int i=0; i<numChildren; i++){
		delete children[i];
	}
	
}

bool Node::addChild(Node* parent, int levels, int totalLevels, float x, float y, float z, float rad)
{
	if(numChildren >= 8){
		return false;
	}

	Node* n = new Node(parent, levels, totalLevels, x, y, z, rad);
	children[numChildren] = n;
	numChildren++;

	return true;
}

int Node::getChildNumber(Node* node)
{
	for(int i=0;i<numChildren;i++){
		if(children[i] == node) return i;
	}
	return -1;
}

void Node::fizzle(){/*
	Vector3* blf = new Vector3(location.x-radius,location.y-radius,location.z+radius);
	Vector3* blb = new Vector3(location.x-radius,location.y-radius,location.z-radius);
	Vector3* brb = new Vector3(location.x+radius,location.y-radius,location.z-radius);
	Vector3* brf = new Vector3(location.x+radius,location.y-radius,location.z+radius);
	Vector3* tlf = new Vector3(location.x-radius,location.y+radius,location.z+radius);
	Vector3* tlb = new Vector3(location.x-radius,location.y+radius,location.z-radius);
	Vector3* trb = new Vector3(location.x+radius,location.y+radius,location.z-radius);
	Vector3* trf = new Vector3(location.x+radius,location.y+radius,location.z+radius);
	glPushMatrix();
	glLoadIdentity();
	glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
	glDisable(GL_CULL_FACE);
	glBegin(GL_LINES);
	glColor3f(0,0,0);/*
	glVertex3f(blf->x,blf->y,blf->z);
	glVertex3f(tlf->x,tlf->y,tlf->z);
	glVertex3f(tlb->x,tlb->y,tlb->z);
	glVertex3f(blb->x,blb->y,blb->z);

	glVertex3f(brf->x,brf->y,brf->z);
	glVertex3f(trf->x,trf->y,trf->z);
	glVertex3f(trb->x,trb->y,trb->z);
	glVertex3f(brb->x,brb->y,brb->z);
	

	glVertex3f(blf->x,blf->y,blf->z);
	glVertex3f(tlf->x,tlf->y,tlf->z);

	glVertex3f(blb->x,blb->y,blb->z);
	glVertex3f(tlb->x,tlb->y,tlb->z);

	glVertex3f(brf->x,brf->y,brf->z);
	glVertex3f(trf->x,trf->y,trf->z);

	glVertex3f(brb->x,brb->y,brb->z);
	glVertex3f(trb->x,trb->y,trb->z);



	glEnd();
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	glEnable(GL_CULL_FACE);
	glPopMatrix();*/
}

void Node::render(int mode, Frustum& frustum)
{
	//fizzle();
	
	if(!frustum.boxInFrustum(location.x,location.y,location.z,size*2,size*2,size*2)){
		return;
	}
	
	std::list<Entity*>::iterator it;
	std::list<TerrainClump*>::iterator cit;
	Entity* entity;
	TerrainClump* clump;
	for (cit=clumps->begin() ; cit != clumps->end(); cit++ ){
		clump = *cit;
		if (frustum.sphereInFrustum(clump->center.x,clump->center.y,clump->center.z,clump->radius)){
			clump->render();
		}
	}

	for (it=entities->begin() ; it != entities->end(); it++ ){
		entity = *it;
		if (entity->getType() == PLAYER && mode == 0){
			continue;
		}

		Vector3 pos = entity->getPosition();

		if (entity->getCollider() == NULL){
			entity->render();
			entity->postRender();
		}
		else if (entity->getCollider()->getType() == SPHERE){
			if(frustum.sphereInFrustum(pos.x, pos.y, pos.z, entity->getCollider()->getRadius())){
				entity->render();
				entity->postRender();
			}
		}
		else if(entity->getCollider()->getType() == AABB){
			AABBCollider* c = (AABBCollider*)entity->getCollider();
			if(frustum.boxInFrustum(pos.x,pos.y,pos.z,c->getWidth(),c->getHeight(),c->getDepth())){
				entity->render();
				entity->postRender();
			}
		}
	}

	for(int i=0; i<numChildren; i++){
		children[i]->render(mode, frustum);
	}

}



bool Node::entityInNode(Entity* ent)
{/*
	Vector3 entityPosition = ent->getPosition();
	float entityRadius = ent->getCollider()->getRadius();

    if ((entityPosition - location).length() <= (entityRadius + size))
    {
        return true;
    }

    return false;*/
	if(ent->getCollider()->getType() == SPHERE){
		float sqDist = Physics::sqDistPointToBox(ent->getPosition().x,ent->getPosition().y,ent->getPosition().z,location.x,location.y,location.z,size,size,size);

		return sqDist <= ent->getCollider()->getRadius() * ent->getCollider()->getRadius();
	}
	else if(ent->getCollider()->getType() == AABB){
		AABBCollider* c = (AABBCollider*)ent->getCollider();
		Vector3 pos = ent->getPosition();
		float minx = pos.x - (c->getWidth() / 2.0f);
		float maxx = pos.x + (c->getWidth() / 2.0f);
		float miny = pos.y - (c->getHeight() / 2.0f);
		float maxy = pos.y + (c->getHeight() / 2.0f);
		float minz = pos.z - (c->getDepth() / 2.0f);
		float maxz = pos.z + (c->getDepth() / 2.0f);
		
		return (maxx >= location.x - size && minx <= location.x + size && maxy >= location.y - size && miny <= location.y + size && maxz >= location.z - size && minz <= location.z + size);
	}

	return false;
}

bool Node::clumpInNode(TerrainClump* clump)
{
	float sqDist = Physics::sqDistPointToBox(clump->center.x,clump->center.y,clump->center.z,location.x,location.y,location.z,size,size,size);
	return sqDist <= clump->radius * clump->radius;
}

void Node::putInNode(Entity* ent)
{
	if(numChildren == 0){ //if it's a leaf node, add the entity
		entities->push_back(ent);
		return;
	}

	int inNodeCount = 0;
	int nodeIndex = -1;

	for(int i=0; i<8; i++){
		if(children[i]->entityInNode(ent)){ //check if entity is in the node
			inNodeCount++;
			nodeIndex = i;
			if(inNodeCount > 1){ //if the entity is in 2 nodes, add it to the parent
				entities->push_back(ent);
				return;
			}
		}
	}
	if(nodeIndex != -1){
		children[nodeIndex]->putInNode(ent); //if it's only in 1 node, add it to that node
	}
	else{
		//m_parent->getEntities()->push_back(ent);
	}
}

void Node::putInNode(TerrainClump* clump)
{
	if(numChildren == 0){
		clumps->push_back(clump);
		return;
	}

	int inNodeCount = 0;
	int nodeIndex = -1;

	for(int i=0; i<8; i++){
		if(children[i]->clumpInNode(clump)){ //check if entity is in the node
			inNodeCount++;
			nodeIndex = i;
			if(inNodeCount > 1){ //if the entity is in 2 nodes, add it to the parent
				clumps->push_back(clump);
				return;
			}
		}
	}
	if(nodeIndex != -1){
		children[nodeIndex]->putInNode(clump); //if it's only in 1 node, add it to that node
	}
	else{
		//m_parent->getTerrainClumps()->push_back(clump);
	}
}

void Node::removeAllEntities()
{
	for(int i=0; i<numChildren; i++){
		children[i]->removeAllEntities();
	}
	entities->clear();
}